﻿using System;
using System.Collections.ObjectModel;
using System.Collections.Generic;

namespace WebProjectOrder.WebServices
{
    public class Account
    {
        #region Atributos de la clase Cuenta
        private int _AccountID;
        private String _Name;
        private String _ForeignName;
        private String _AccountCode;
        private Constants.AccountType _AccountType;
        private Constants.SpecialType _SpecialType;
        private bool _IsTitle;
        private bool _AlDebe;
        private Currency _Currency;
        private Double _LocalAmount;
        private Double _SysAmount;
        private Account _Parent;

        /// <summary>
        /// Colección que contiene los nodos Cuenta hijos,
        /// si es que la cuenta es un título, sino será vacía
        /// </summary>
        private Dictionary<int, Account> _Children;
        #endregion

        #region Constructor de la clase cuenta
        /// <summary>
        ///  Constructor vacío
        /// </summary>
        public Account()
        {
            // Todas las cuentas son normales por defecto
            _SpecialType = Constants.SpecialType.normal;
        }
        #endregion

        #region Propiedades de la clase Cuenta

        public Constants.SpecialType SpecialType
        {
            get { return _SpecialType; }
            set { _SpecialType = value; }
        }

        /// <summary>
        /// Es el nombre con el que la cuenta se despliega en la interfaz (Código + Nombre)
        /// </summary>
        public String DisplayName
        {
            get { return _AccountCode + " " + _Name; }
        }

        /// <summary>
        /// Retorna el ID de la cuenta
        /// </summary>
        public int AccountID
        {
            get { return _AccountID; }
            set { _AccountID = value; }
        }

        /// <summary>
        /// Retorna y Setea el nombre de la cuenta
        /// </summary>
        public String Name
        {
            get { return _Name; }
            set { _Name = value; }
        }

        /// <summary>
        /// Retrona y setea el nombre extranjero de la cuenta
        /// </summary>
        public String ForeignName
        {
            get { return _ForeignName; }
            set { _ForeignName = value; }
        }

        /// <summary>
        /// Retorna el código de la cuenta
        /// </summary>
        public String AccountCode
        {
            get { return _AccountCode; }
            set { _AccountCode = value; }
        }

        /// <summary>
        /// Retorna y setea el tipo de cuenta que es
        /// </summary>
        public Constants.AccountType AccountType
        {
            get { return _AccountType; }
            set { _AccountType = value; }
        }

        /// <summary>
        /// Retorna el booleano que representa si la cuenta es un título o no
        /// </summary>
        public bool IsTitle
        {
            get { return _IsTitle; }
            set { _IsTitle = value; }
        }

        /// <summary>
        /// Dice si la cuenta está al debe o al haber
        /// </summary>
        public bool AlDebe
        {
            get { return _AlDebe; }
            set { _AlDebe = value; }
        }

        // Retorna y setea la moneda
        public Currency AccountCurrency
        {
            get { return _Currency; }
            set { _Currency = value; }
        }

        public Account Parent
        {
            get { return _Parent; }
            set { _Parent = value; }
        }

        /// <summary>
        /// Retorna la lista de hijos Cuenta
        /// </summary>
        public Dictionary<int, Account> Children
        {
            get { return _Children; }
            set { _Children = value; }
        }

        /// <summary>
        /// Monto correspondiente al saldo de la cuenta en la moneda del sistema
        /// </summary>
        public double SysAmount
        {
            get { return _SysAmount; }
            set { _SysAmount = value; }
        }

        /// <summary>
        /// Monto correspondiente al saldo de la cuenta en moneda local
        /// </summary>
        public double LocalAmount
        {
            get { return _LocalAmount; }
            set { _LocalAmount = value; }
        }

        /// <summary>
        /// Monto correspondiente al saldo de la cuenta según su moneda
        /// </summary>
        public double AccountCurrencyAmount
        {
            get
            {
                return _LocalAmount / _Currency.ToLocalCurrency;
            }
        }
        #endregion

        #region Métodos de la clase Cuenta
        /// <summary>
        /// Agrega una cuenta como hijo de esta cuenta y actualiza los montos locales, del sistema y el tipo de incremento
        /// </summary>
        /// <param name="addedAccount">Cuenta que va a ser hija de esta cuenta</param>
        public void AddAccount(Account addedAccount)
        {
            _Children.Add(addedAccount.AccountID, addedAccount);
            addedAccount.Parent = this;
            UpdateAmount(addedAccount.AlDebe, addedAccount.SysAmount, addedAccount.LocalAmount);
        }

        public void UpdateAmount(bool alDebe, double sysAmount, double localAmount)
        {
            double multiplier = _AlDebe ^ alDebe ? -1 : 1;
            _SysAmount += sysAmount * multiplier;
            _LocalAmount += localAmount * multiplier;
            if (_LocalAmount < 0)
            {
                _LocalAmount *= -1;
                _SysAmount *= -1;
                _AlDebe = !_AlDebe;
            }
            if(_Parent != null)
                _Parent.UpdateAmount(alDebe, sysAmount, localAmount);
        }

        public Account GetAccount(int accountId)
        {
            if (_Children == null)
                return null;
            else if (_AccountID == accountId)
                return this;
            else if (_Children.ContainsKey(accountId))
                return _Children[accountId];
            else
                foreach (Account child in _Children.Values)
                {
                    Account possibleAccount = child.GetAccount(accountId);
                    if (possibleAccount != null)
                        return possibleAccount;
                }
            return null;
        }

        /// <summary>
        /// Método que devuelve la cuenta del mismo código que el dado
        /// </summary>
        /// <param name="accountCode"></param>
        /// <returns></returns>
        public Account GetAccount(String accountCode)
        {
            if (_Children == null)
                return null;
            else if (_AccountCode == accountCode)
                return this;
            else
                foreach (Account child in _Children.Values)
                {
                    if (child.AccountCode == accountCode)
                        return child;
                    else
                    {
                        Account possibleAccount = child.GetAccount(accountCode);
                        if (possibleAccount != null)
                            return possibleAccount;
                    }
                }
            return null;
        }
        #endregion
    }
}
